package subscribe

import (
	"errors"
	"fmt"
	"manager/config"
	"manager/control"
	"manager/mqtt/publish"

	MQTT "github.com/eclipse/paho.mqtt.golang"
	"hiscene.com/openplatform/common/logger"
)

////////////////////////////////////////////////////////////////////////////////
var (
	client  MQTT.Client
	stop    chan bool
	subkeys map[string]byte
)

// 错误定义
var (
	ErrTokenCreat = errors.New("token create error")
	ErrTokenConn  = errors.New("token connect error")
)

////////////////////////////////////////////////////////////////////////////////

// Start 启动
func Start(opts *MQTT.ClientOptions) error {
	opts.SetOnConnectHandler(onConnect)
	opts.SetConnectionLostHandler(lostConnect)
	if len(config.MQTTCfg.ID) > 0 {
		opts.SetClientID(fmt.Sprintf("%v_subscribe", config.MQTTCfg.ID))
	}

	client = MQTT.NewClient(opts)
	token := client.Connect()
	if token == nil || token.Wait() == false {
		logger.Log4g.Error("subscribe start connect token fail")
		return ErrTokenCreat
	}

	err := token.Error()
	if err != nil {
		logger.Log4g.Error("subscribe start connect token error: %v", err)
		return err
	}

	subkeys = map[string]byte{
		config.MQTTCfg.CTOSTopic + "/#": 2,
	}

	stop = make(chan bool)
	go func() {
		for {
			select {
			case <-stop:
				{
					goto EXIT
				}
			}
		}
	EXIT:
		client.Disconnect(250)
		client = nil
		stop <- true
	}()
	return nil
}

// Stop 关闭
func Stop() {
	opts := client.OptionsReader()
	logger.Log4g.Trace("mqtt subscribe client id: %v stop service", opts.ClientID())

	stop <- true
	<-stop
	close(stop)
}

////////////////////////////////////////////////////////////////////////////////

func onConnect(clt MQTT.Client) {
	opts := clt.OptionsReader()
	logger.Log4g.Trace("subscribe mqtt connected, client id: %v, address: %v", opts.ClientID(), opts.Servers())

	if token := client.SubscribeMultiple(subkeys, receiveData); token.Wait() && token.Error() != nil {
		logger.Log4g.Error("subscribe on connect token error: %v", token.Error())
		return
	}
}

func lostConnect(clt MQTT.Client, err error) {
	opts := clt.OptionsReader()
	logger.Log4g.Trace("subscribe mqtt lost connect, client id: %v, address: %v, error: %v", opts.ClientID(), opts.Servers(), err)

	if config.MQTTCfg.Reconnect == false {
		token := client.Connect()
		if token == nil || token.Wait() == false {
			logger.Log4g.Error("subscribe disconnect try connect token fail")
			return
		}

		err = token.Error()
		if err != nil {
			logger.Log4g.Error("subscribe disconnect try connect token error: %v", err)
			return
		}
	}
}

////////////////////////////////////////////////////////////////////////////////

func receiveData(clt MQTT.Client, msg MQTT.Message) {
	topic := msg.Topic()
	topiclen := len(topic)
	if topiclen >= config.MQTTCfg.CTOSTopicLen && topic[:config.MQTTCfg.CTOSTopicLen] == config.MQTTCfg.CTOSTopic {
		data := string(msg.Payload())
		info := &control.TickInfo{}
		info.Init(data)

		control.RefreshBoard(info)
		if info.Sync == true {
			state := control.GetBoardState(info.Mac)
			if len(state) > 0 {
				publish.Send(info.Mac, "STATE", state)
			}
		}
	}
}
